/*******************************************************************************
 * Copyright (c) 2008, 2009 Tapestry IDE Developers
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 * 
 * Contributors:
 *     Tapestry IDE Developers - initial API and implementation
 *******************************************************************************/
package com.googlecode.tapestry.ide.internal.ui.navigator;

import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.jface.viewers.StructuredViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.swt.widgets.Control;
import org.eclipse.ui.IMemento;
import org.eclipse.ui.navigator.ICommonContentExtensionSite;
import org.eclipse.ui.navigator.ICommonContentProvider;

import com.googlecode.tapestry.ide.core.model.IModelChangeListener;
import com.googlecode.tapestry.ide.core.model.IModelElement;
import com.googlecode.tapestry.ide.core.model.ITapestryModel;
import com.googlecode.tapestry.ide.core.model.ModelChangeEvent;
import com.googlecode.tapestry.ide.core.model.ModelChangeEvent.Type;

/**
 * The Class AbstractNavigatorContentProvider.
 */
public abstract class AbstractNavigatorContentProvider implements
		ICommonContentProvider, IModelChangeListener {

	/** The tapestry model. */
	protected ITapestryModel tapestryModel;

	/** The viewer. */
	private StructuredViewer viewer;

	/**
	 * Instantiates a new abstract navigator content provider.
	 * 
	 * @param tapestryModel
	 *            the tapestry model
	 */
	public AbstractNavigatorContentProvider(ITapestryModel tapestryModel) {
		super();
		this.tapestryModel = tapestryModel;
	}

	/**
	 * Gets the viewer.
	 * 
	 * @return the viewer
	 */
	protected final StructuredViewer getViewer() {
		return viewer;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.eclipse.ui.navigator.ICommonContentProvider#init(org.eclipse.ui.navigator
	 * .ICommonContentExtensionSite)
	 */
	@Override
	public void init(ICommonContentExtensionSite aConfig) {
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.eclipse.jface.viewers.ITreeContentProvider#getChildren(java.lang.
	 * Object)
	 */
	@Override
	public Object[] getChildren(Object parentElement) {
		return IModelElement.NO_CHILDREN;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.eclipse.jface.viewers.ITreeContentProvider#getParent(java.lang.Object
	 * )
	 */
	@Override
	public Object getParent(Object element) {
		return null;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.eclipse.jface.viewers.ITreeContentProvider#hasChildren(java.lang.
	 * Object)
	 */
	@Override
	public boolean hasChildren(Object element) {
		return getChildren(element).length > 0;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.eclipse.jface.viewers.IStructuredContentProvider#getElements(java
	 * .lang.Object)
	 */
	@Override
	public Object[] getElements(Object inputElement) {
		return getChildren(inputElement);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.jface.viewers.IContentProvider#dispose()
	 */
	@Override
	public void dispose() {
		tapestryModel.removeChangeListener(this);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse.jface
	 * .viewers.Viewer, java.lang.Object, java.lang.Object)
	 */
	@Override
	public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
		if (viewer instanceof StructuredViewer) {
			this.viewer = (StructuredViewer) viewer;
			if (oldInput == null && newInput != null) {
				tapestryModel.addChangeListener(this);
			} else if (oldInput != null && newInput == null) {
				tapestryModel.removeChangeListener(this);
			}

		} else {
			this.viewer = null;
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.eclipse.ui.navigator.IMementoAware#restoreState(org.eclipse.ui.IMemento
	 * )
	 */
	@Override
	public void restoreState(IMemento aMemento) {

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.eclipse.ui.navigator.IMementoAware#saveState(org.eclipse.ui.IMemento)
	 */
	@Override
	public void saveState(IMemento aMemento) {

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.googlecode.tapestry.ide.core.model.IModelChangeListener#elementChanged
	 * (com.googlecode.tapestry.ide.core.model.ModelChangeEvent)
	 */
	@Override
	public void elementChanged(ModelChangeEvent event) {
		IModelElement element = event.getElement();

		// For events of type ADDED or REMOVED refresh the parent of the changed
		// model element
		if (event.getType() == Type.CHANGED) {
			refreshViewerForElement(element);
		} else {
			if (element.getElementParent() instanceof ITapestryModel) {
				refreshViewerForElement(ResourcesPlugin.getWorkspace()
						.getRoot());
			} else {
				refreshViewerForElement(element.getElementParent());
			}
		}
	}

	/**
	 * Refresh viewer for element.
	 * 
	 * @param element
	 *            the element
	 */
	protected final void refreshViewerForElement(final Object element) {
		if (viewer instanceof StructuredViewer && element != null) {

			// Abort if this happens after disposes
			Control ctrl = viewer.getControl();
			if (ctrl == null || ctrl.isDisposed()) {
				return;
			}

			// Are we in the UI thread?
			if (ctrl.getDisplay().getThread() == Thread.currentThread()) {
				viewer.refresh(element);
			} else {
				ctrl.getDisplay().asyncExec(new Runnable() {
					public void run() {

						// Abort if this happens after disposes
						Control ctrl = viewer.getControl();
						if (ctrl == null || ctrl.isDisposed()) {
							return;
						}
						if (element instanceof ITapestryModel) {
							viewer.refresh();
						} else {
							viewer.refresh(element);
						}
					}
				});
			}
		}
	}

}
